package it.softphone.commons.gdata;

import static it.softphone.commons.util.LogUtil.logger;

import it.softphone.commons.exception.NullParameterException;
import it.softphone.commons.util.LogUtil;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.security.auth.login.LoginException;
import javax.security.sasl.AuthenticationException;

import org.apache.commons.configuration.Configuration;

import com.google.gdata.client.docs.DocsService;
import com.google.gdata.data.Link;
import com.google.gdata.data.MediaContent;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.acl.AclEntry;
import com.google.gdata.data.acl.AclRole;
import com.google.gdata.data.acl.AclScope;
import com.google.gdata.data.docs.DocumentEntry;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.docs.DocumentListFeed;
import com.google.gdata.data.docs.FolderEntry;
import com.google.gdata.util.ServiceException;

public class GDataManager  {
	  private static final String DEFAULT_AUTH_PROTOCOL = "https";
	  private static final String DEFAULT_AUTH_HOST = "docs.google.com";

	  private static final String DEFAULT_PROTOCOL = "http";
	  private static final String DEFAULT_HOST = "docs.google.com";

	  private final String URL_FEED = "/feeds";
	  private final String URL_DOWNLOAD = "/download";
	  private final String URL_DOCLIST_FEED = "/private/full";

	  private final String URL_DEFAULT = "/default";
	  private final String URL_FOLDERS = "/contents";
	  private final String URL_ACL = "/acl";
	  private final String URL_REVISIONS = "/revisions";

	  private final String URL_CATEGORY_DOCUMENT = "/-/document";
	  private final String URL_CATEGORY_SPREADSHEET = "/-/spreadsheet";
	  private final String URL_CATEGORY_PDF = "/-/pdf";
	  private final String URL_CATEGORY_PRESENTATION = "/-/presentation";
	  private final String URL_CATEGORY_STARRED = "/-/starred";
	  private final String URL_CATEGORY_TRASHED = "/-/trashed";
	  private final String URL_CATEGORY_FOLDER = "/-/folder";
	  private final String URL_CATEGORY_EXPORT = "/Export";

	  private final String PARAMETER_SHOW_FOLDERS = "showfolders=true";
	  
	  private final DocsService service;

	  private final String protocol;
	  private final String host;
	  private final String applicationName ;
	  
	  /**
	   * 
	   * @param applicationName
	   * @param authProtocol
	   * @param authHost
	   * @param protocol
	   * @param host
	   * @return
	   */
	  public static GDataManager createInstance( String applicationName, String protocol, String host )  {
		  return new GDataManager(applicationName, protocol, host);
	  }
	  
	  /**
	   * 
	   * @param config
	   * @return
	   */
	  public static GDataManager createInstance( Configuration config )  {
		  if( null==config ) throw new NullParameterException("config");
		  
		  Configuration _conf = config.subset("gdata");
		  
		  final String applicationName = _conf.getString("applicationName", "commons-report");	  
		  final  String protocol = _conf.getString("protocol", DEFAULT_PROTOCOL);
		  final  String host = _conf.getString("host", DEFAULT_HOST);
		  
		  return new GDataManager(applicationName, protocol, host);
	  }
	  
	/**
	   * Constructor
	   *
	   * @param applicationName name of the application
	   * @param protocol the protocol to use for the http calls.
	   * @param host the host that contains the feeds
	   *
	   * @throws DocumentListException
	   */
	  protected GDataManager(String applicationName,  String protocol, String host)  {
		  if( applicationName == null ) throw new NullParameterException("applicationName");
		  if( protocol == null ) throw new NullParameterException("protocol");
		  if( host == null ) throw new NullParameterException("host");
		  

	    service = new DocsService(applicationName);

	    this.applicationName = applicationName;
	    this.protocol = protocol;
	    this.host = host;
	  }
	  
	  
		public final DocsService getService() {
			return service;
		}

	  
	/**
	   * 
	   * @return
	   */
	  public final String getProtocol() {
		  return protocol;
	  }

	  /**
	   * 
	   * @return
	   */
	  public final String getHost() {
		  return host;
	  }

	  /**
	   * 
	   * @return
	   */
	  public final String getApplicationName() {
		  return applicationName;
	  }

	/**
	   * Set user credentials based on a username and password.
	   *
	   * @param user username to log in with.
	   * @param pass password for the user logging in.
	   *
	   * @throws AuthenticationException
	   * @throws DocumentListException
	   */
	  public void login(String user, String pass) throws LoginException {
		  if( user == null ) throw new NullParameterException("user");
		  if( pass == null ) throw new NullParameterException("pass");

		  try {
			  service.setUserCredentials(user, pass);
		  } catch (com.google.gdata.util.AuthenticationException e) {

			  LogUtil.logger.error( "login error", e );
			  throw new LoginException(e.getMessage());
		  }
	  }
	  
	  /**
	   * 
	   * @param path
	   * @param parameters
	   * @return
	   * @throws MalformedURLException
	   */
	  protected java.net.URL buildUrl(String path, String[] parameters) throws MalformedURLException {

		  StringBuilder url = new StringBuilder();
		  url.append(protocol)
		  	.append("://")
		  	.append( host )
		  	.append( URL_FEED )
		  	.append( path );

		  if (parameters != null && parameters.length > 0) {
			  url.append("?");
			  for (int i = 0; i < parameters.length; i++) {
				  url.append(parameters[i]);
				  if (i != (parameters.length - 1)) {
					  url.append("&");
				  }
			  }
		  }

		  return new java.net.URL(url.toString());
	  }
	  
	  /**
	   * 
	   * @param title
	   * @return
	   * @throws ReportException
	   */
	  protected DocumentListEntry getOrCreatefolder( String title ) throws Exception  {

		  DocumentListEntry result = null;
		  
		  String[] parameters = {PARAMETER_SHOW_FOLDERS, "title="+title};
		  
		  URL feedUrl = null;
		  try {
			  feedUrl = buildUrl(URL_DEFAULT + URL_DOCLIST_FEED + URL_CATEGORY_FOLDER, parameters);
		  } catch (MalformedURLException e) {
			  final String msg = "error building URL";
			  logger.error( msg, e );
			throw new Exception(msg, e);
		  }


		  DocumentListFeed f = null;
		  try {
			  f = service.getFeed(feedUrl, DocumentListFeed.class );
		  } catch (Exception e) {
			  final String msg = String.format("error getting feed from [%s]", feedUrl.toString() );
			  logger.error( msg, e );
			  throw e;
		  }

		  java.util.List<DocumentListEntry> entries = f.getEntries();
		  
		  if( entries.isEmpty()  ) {
			  // CREATE FOLDER
			  
				DocumentListEntry newEntry = new FolderEntry();

				newEntry.setTitle(new PlainTextConstruct(title));
				
				java.net.URL createFolderUrl = null;
				try {
					createFolderUrl = new java.net.URL( 
							new StringBuilder()
								.append(protocol)
								.append("://")
								.append(host)
								.append( URL_FEED )
								.append( URL_DEFAULT ) 
								.append( URL_DOCLIST_FEED)
								.toString() );
					
					result =  service.insert(createFolderUrl, newEntry);
				} catch (MalformedURLException e) {
					  final String msg = "error building URL";
					  logger.error( msg, e );
					throw e;
				} catch (Exception e) {
					  final String msg = String.format("error creating folder [%s]", createFolderUrl.toString() );
					  logger.error( msg, e );
					  throw e;
				}
				
			  
		  }
		  else if( entries.size() > 1 ){
			  throw new DuplicateFolderNameException(title);
		  }
		  else {
			 
			 result = entries.get(0); 
		  }

		  return result;
	  }

	  /**
	   * 
	   * @param aclFeedLink
	   * @param aclUri
	   * @throws ReportException
	 * @throws ServiceException 
	 * @throws IOException 
	   */
	  public AclEntry addShareReader( java.net.URL aclFeedLink, java.net.URI aclUri ) throws IOException, ServiceException {
		  if( aclFeedLink == null ) throw new NullParameterException("aclFeedLink");
		  if( aclUri == null ) throw new NullParameterException("aclUri");
		  
		  String scheme = aclUri.getScheme();
		  String contact = aclUri.getSchemeSpecificPart();
		  
		  AclScope scope = null;
		  
		  if( "user".equalsIgnoreCase(scheme)) {
			  
			  scope = new AclScope( AclScope.Type.USER, contact);
			  logger.info( String.format("document [%s] shared with user [%s]", aclFeedLink.toString(), contact) );
		  }
		  else if( "group".equalsIgnoreCase(scheme)) {
			  
			  scope = new AclScope( AclScope.Type.GROUP, contact);
			  logger.info( String.format("document [%s] shared with group [%s]", aclFeedLink.toString(), contact) );
		  }
		  else if( "domain".equalsIgnoreCase(scheme)) {
			  scope = new AclScope( AclScope.Type.DOMAIN, contact);
			  logger.info( String.format("document [%s] shared with domain [%s]", aclFeedLink.toString(), contact) );
			  
		  }
		  else { // DEFAULT
			  scope = new AclScope( AclScope.Type.DEFAULT, contact);
			  logger.info( String.format("document [%s] shared with [%s]", aclFeedLink.toString(), contact) );
			  
		  }
		  
		  
		  AclEntry entry  = new AclEntry();
		  entry.setRole( AclRole.READER );
		  entry.setScope(scope);
			
			  
		  return service.insert( aclFeedLink, entry );
		  
	  }
	  
	  /**
	   * 
	   * @param document
	   * @param title
	   * @param aclScope
	   * @return
	   * @throws ReportException
	   */
	  public DocumentEntry uploadDocument( java.io.File document, String title,  java.net.URI ...aclScopes ) throws Exception {
		  if( document == null ) throw new NullParameterException("report");
		  if( title == null ) throw new NullParameterException("title");
		  
		  
		  DocumentListEntry folderEntry = getOrCreatefolder(applicationName);
		  
		  String mimeType = DocumentListEntry.MediaType.fromFileName(document.getName()).getMimeType();

		  String destFolderUri = ((MediaContent) folderEntry.getContent()).getUri();

		  DocumentEntry newDocument = new DocumentEntry();
		  newDocument.setFile(document, mimeType);
		  newDocument.setTitle(new PlainTextConstruct(title));
		  newDocument.setWritersCanInvite(true);
		    
		  DocumentEntry result = null;
		  try {
			  result = service.insert( new java.net.URL(destFolderUri), newDocument );

		  } catch (MalformedURLException e) {
			  final String msg = "error building URL";
			  logger.error( msg, e );
			  throw e;
		  } catch (Exception e) {
			  final String msg = String.format("error creating document [%s]", destFolderUri );
			  logger.error( msg, e );
			  throw e;
		  }
			
		  if( logger.isDebugEnabled() ) {
			  Link  link = result.getDocumentLink();
			  logger.debug( String.format("link href=[%s]\n", link.getHref()) );
		  }
		
			if (aclScopes != null) {
				java.net.URL aclFeedLink = null;
				try {
					aclFeedLink = new java.net.URL(result.getAclFeedLink()
							.getHref());
				} catch (MalformedURLException e) {
					final String msg = "error building aclFeedLink URL";
					logger.error(msg, e);
				}
	
				for (java.net.URI aclUri : aclScopes) {
					try {
						addShareReader(aclFeedLink, aclUri);
					} catch (Exception e) {
						final String msg = String.format(
								"error creating sharing reader on [%s] for [%s]",
								aclFeedLink, aclUri);
						logger.error(msg, e);
					}
				}
			}
		  return result;
	  }
}
